﻿using System.Collections.Generic;
using BlawkayEngine.Input;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace BlawkayEngine.ScreenSystem
{
    public abstract class ScreenManager
    {

        #region Constructor

        public ScreenManager(Game oGame)
        {
            _Game = oGame;
            _Screens = new List<Screen>();
            _AddScreens = new List<Screen>();
            _RemoveScreens = new List<Screen>();
            _SpriteBatch = new SpriteBatch(oGame.GraphicsDevice);
        }

        #endregion

        #region Methods

        public virtual void Update()
        {

            //// -- Adds Screens -- ////
            if (ScreensToAdd.Count > 0)
            {
                // Adds Screens to list of screens to Add
                foreach (Screen oScreen in ScreensToAdd)
                {
                    if (Screens.Contains(oScreen) == false)
                    {
                        Screens.Add(oScreen);
                    }
                }

                // Allow Removal of Screens before Addition
                if (ScreensToRemove.Count == 0)
                {
                    List<Screen> oRemove = new List<Screen>();

                    // Increases transition of all added screens
                    foreach (Screen oScreen in ScreensToAdd)
                    {
                        oScreen.Transition += oScreen.TransitionRate;
                        if (oScreen.Transition >= 1)
                        {
                            oScreen.Transition = 1.0f;
                            oRemove.Add(oScreen);
                        }
                    }

                    // Removes from Added List if completely transitioned
                    // Allows an initial action when completely added
                    foreach (Screen oScreen in oRemove)
                    {
                        oScreen.ActionWhenFullyAdded(this);
                        ScreensToAdd.Remove(oScreen);
                    }
                    oRemove.Clear();
                }
            }

            //// -- Removes Screens -- ////
            if (ScreensToRemove.Count > 0)
            {
                List<Screen> oRemove = new List<Screen>();

                // Decreases Transition of Screens before Removal
                foreach (Screen oScreen in ScreensToRemove)
                {
                    oScreen.Transition -= oScreen.TransitionRate;
                    if (oScreen.Transition <= 0)
                    {
                        if (Screens.Contains(oScreen))
                        {
                            oRemove.Add(oScreen);
                        }
                    }
                }

                // Removes Screen forever
                foreach (Screen oScreen in oRemove)
                {
                    oScreen.ActionWhenFullyRemoved(this);
                    ScreensToRemove.Remove(oScreen);
                    Screens.Remove(oScreen);
                }
                oRemove.Clear();
            }

            //// -- Updates Screens -- ////
            foreach (Screen oScreen in Screens)
            {
                if (oScreen.Enabled)
                    oScreen.Update(this);
            }
        }

        public virtual void Draw()
        {
            _SpriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.AlphaBlend);

            //// -- Draws Screens -- ////
            foreach (Screen oScreen in Screens)
            {
                if (oScreen.Visible)
                    oScreen.Draw(this, _SpriteBatch);
            }

            _SpriteBatch.End();
        }

        public void AddScreen(Screen oScreen)
        {
            oScreen.Transition = 0.0f;
            ScreensToAdd.Add(oScreen);
        }

        public void RemoveScreen(Screen oScreen)
        {
            oScreen.Transition = 1.0f;
            ScreensToRemove.Add(oScreen);

            if (ScreensToAdd.Contains(oScreen))
                ScreensToAdd.Remove(oScreen);
        }

        /// <summary>
        /// Sends the current screens of the Current Manager to the remove list of the New Manager to be transitioned off.
        /// </summary>
        /// <param name="oManager">The Current Manager</param>
        /// <param name="oNewManager">The New Manager</param>
        /// <returns>A New Manager with the Removed Screens of the Current</returns>
        public static ScreenManager SendScreensToNewManager(ScreenManager oManager, ScreenManager oNewManager)
        {
            foreach (Screen oScreen in oManager.Screens)
            {
                oNewManager.Screens.Add(oScreen);
                oNewManager.RemoveScreen(oScreen);
            }

            return oNewManager;
        }

        #endregion

        #region Properties

        public List<Screen> Screens
        {
            get { return _Screens; }
        }

        protected List<Screen> ScreensToAdd
        {
            get { return _AddScreens; }
        }

        protected List<Screen> ScreensToRemove
        {
            get { return _RemoveScreens; }
        }

        public Game Game
        {
            get { return _Game; }
        }

        protected SpriteBatch SpriteBatch
        {
            get { return _SpriteBatch; }
        }

        #endregion

        #region Fields

        private List<Screen> _Screens;
        private List<Screen> _AddScreens;
        private List<Screen> _RemoveScreens;

        private Game _Game;
        private SpriteBatch _SpriteBatch;

        #endregion

    }
}
